package org.luxor.generator.service;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import org.luxor.commons.jdbc.plugin.dynamic.datasource.DynamicDataSourceCreator;
import org.luxor.commons.jdbc.plugin.dynamic.datasource.DynamicRoutingDataSource;
import org.luxor.commons.jdbc.plugin.dynamic.properties.DataSourceProperty;
import org.luxor.generator.constant.DateSourceType;
import org.luxor.generator.constant.GeneratorConstants;
import org.luxor.generator.entity.DataSourceEntity;
import org.luxor.generator.utils.RRException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Optional;


/**
 * 项目数据源配置
 *
 * @author Mark sunlightcs@gmail.com
 */
@Slf4j
@Service
public class SysDataSourceService implements HttpSessionListener {

    private static final String DEFAULT_DS = "master";

    @Resource
    private DynamicDataSourceCreator dynamicDataSourceCreator;
    @Resource
    private DynamicRoutingDataSource dynamicDataSource;
    @Resource
    private HttpSession session;

    @Value("${spring.datasource.driver-class-name:}" )
    private String driverClassName;
    @Value("${spring.datasource.url:}" )
    private String jdbcUrl;
    @Value("${spring.datasource.username:}" )
    private String jdbcUsername;
    @Value("${spring.datasource.password:}" )
    private String password;

    /**
     * 保存,当前的数据源配置
     *
     * @param dataSourceEntity
     * @return
     */
    public DataSourceEntity save(DataSourceEntity dataSourceEntity) {
        DataSourceEntity sessionVal = new DataSourceEntity();
        BeanUtils.copyProperties(getDefaultDatasource(), sessionVal);
        BeanUtils.copyProperties(dataSourceEntity, sessionVal);

        // 生成数据源名称
        String ds = produceDataSourceName(sessionVal);

        // 提取数据源名称
        Object sessionAttr = session.getAttribute(GeneratorConstants.DATASOURCE_DB_KEY);
        if (sessionAttr != null && !"master".equals(sessionAttr)) {
            dynamicDataSource.removeDataSource((String) sessionAttr);
        }

        String jdbcUrl = StringEscapeUtils.unescapeHtml(dataSourceEntity.getUrl());
        // 创建新数据源
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        dataSourceProperty.setDriverClassName(extDriverClassName(jdbcUrl));
        dataSourceProperty.setUrl(jdbcUrl);
        dataSourceProperty.setUsername(dataSourceEntity.getUsername());
        dataSourceProperty.setPassword(dataSourceEntity.getPassword());
        dataSourceProperty.getHikari().setMaxPoolSize(2);
        dataSourceProperty.setPollName(ds);
        DataSource dataSource = dynamicDataSourceCreator.createDataSource(dataSourceProperty);

        // 添加新数据源
        dynamicDataSource.addDataSource(ds, dataSource);

        session.setAttribute(GeneratorConstants.DATASOURCE_DB_KEY, ds);
        session.setAttribute(GeneratorConstants.DATASOURCE_CONFIG_KEY, sessionVal);
        return dataSourceEntity;
    }


    /**
     * 获取,当前的数据源配置
     *
     * @return
     */
    public DataSourceEntity info() {
        Object sessionAttr = session.getAttribute(GeneratorConstants.DATASOURCE_CONFIG_KEY);
        if (sessionAttr != null && sessionAttr instanceof DataSourceEntity) {
            return (DataSourceEntity) sessionAttr;
        }
        return getDefaultDatasource();
    }

    /**
     * 重置,当前数据源配置
     *
     * @return
     */
    public DataSourceEntity reset() {
        Object sessionAttr = session.getAttribute(GeneratorConstants.DATASOURCE_DB_KEY);
        if (sessionAttr != null && sessionAttr instanceof String) {
            dynamicDataSource.removeDataSource((String) sessionAttr);
        }
        session.removeAttribute(GeneratorConstants.DATASOURCE_CONFIG_KEY);
        return getDefaultDatasource();
    }


    private DataSourceEntity getDefaultDatasource() {
        DataSourceEntity defDataSource = new DataSourceEntity();
        Optional<DateSourceType> databaseType = Arrays.stream(DateSourceType.values()).filter(
                el -> driverClassName.indexOf(el.name()) != -1).findFirst();
        defDataSource.setDatabaseType(databaseType.get());
        defDataSource.setUrl(jdbcUrl);
        defDataSource.setUsername(jdbcUsername);
        defDataSource.setPassword(password);
        return defDataSource;
    }

    private String extDriverClassName(String jdbcUrl) {
        Optional<DateSourceType> databaseType = Arrays.stream(DateSourceType.values()).filter(
                el -> jdbcUrl.toLowerCase().indexOf(el.name()) != -1).findFirst();
        if (DateSourceType.mysql.equals(databaseType.get())) {
            return GeneratorConstants.DRIVERCLASSNAMES[0];
        } else if (DateSourceType.oracle.equals(databaseType)) {
            return GeneratorConstants.DRIVERCLASSNAMES[1];
        } else if (DateSourceType.sqlserver.equals(databaseType)) {
            return GeneratorConstants.DRIVERCLASSNAMES[2];
        } else if (DateSourceType.postgresql.equals(databaseType)) {
            return GeneratorConstants.DRIVERCLASSNAMES[3];
        } else {
            throw new RRException("不支持当前数据库：" + databaseType);
        }
    }

    private String produceDataSourceName(DataSourceEntity property) {
        String dsVal = String.format("%s:%s:%s:%s", session.getId(), property.getUrl(), property.getUsername(), property.getPassword());
        return String.valueOf(dsVal.hashCode());
    }

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        // 初始化数据源为master
        httpSessionEvent.getSession().setAttribute(GeneratorConstants.DATASOURCE_DB_KEY, DEFAULT_DS);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        Object sessionAttr = httpSessionEvent.getSession().getAttribute(GeneratorConstants.DATASOURCE_DB_KEY);
        if (sessionAttr != null && sessionAttr instanceof String) {
            if (!DEFAULT_DS.equals(sessionAttr)) {
                dynamicDataSource.removeDataSource((String) sessionAttr);
            }
        }
    }
}
